﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;


namespace AIthin.Neuro
{
    [Serializable]
    public class Layer
    {
        Network parentNetwork;
        float X;
        float Y;
        float Z;
        float xRange;
        float yRange;
        protected static Random random = new Random();
        /// <summary>
        /// bind range difference
        /// </summary>
        static float connectionsDistance = 0.6f;

        public List<int> layerNeurons {get; internal set;}

        public Layer(Network PN, float x, float y, float z, float XRange, float YRange, int newNeurons)
        {
            parentNetwork = PN;
            X = x;
            Y = y;
            Z = z;
            xRange = XRange;
            yRange = YRange;
            layerNeurons = new List<int>();

            float iX, iY, iZ;

            for (int i = 0; i < newNeurons; i++)
            {
                iX = ((float)random.NextDouble() - 0.5f) * xRange + X;
                iY = ((float)random.NextDouble() - 0.5f) * yRange + Y;
                iZ = Z;
                parentNetwork.AddNeuron(
                    new Neuron(parentNetwork.NeuronsCount, 1, 1, iX, iY, iZ));

                layerNeurons.Add(parentNetwork.NeuronsCount - 1);
            }

            parentNetwork.layers.Add(this);

        }

        public Layer(Network PN, float x, float y, float z, float XRange, float YRange,
            int newNeurons, float bindRange, int maxConnected)
            : this(PN, x, y, z, XRange, YRange, newNeurons)
        {
            int[] inputNeurons;

            foreach (int n in layerNeurons)
            {

                inputNeurons
                    = parentNetwork.findNeuronsInRange(
                    parentNetwork[n].X, parentNetwork[n].Y, parentNetwork[n].Z,
                    bindRange);

                //all connections be connected when maxConnected < 1
                if (maxConnected > 0)
                {
                    List<int> connectNeurons = new List<int>();

                    connectNeurons.AddRange(inputNeurons);

                    while (connectNeurons.Count > maxConnected)
                        connectNeurons.RemoveAt(random.Next(connectNeurons.Count));

                    inputNeurons = connectNeurons.ToArray();
                }

                foreach (int i in inputNeurons)
                {
                    if (!parentNetwork.neurons[n].ConnectToOutputOf(ref parentNetwork.neurons[i]))
                    {
                            Debug.WriteLine("neuron " + n.ToString() + " not connected to: " + i.ToString());
                    }
                }


            }

            return;
        }

        public Layer(Network PN, Layer layer, float x, float y, float z, float XRange, float YRange,
           int newNeurons, int maxConnected)
            : this(PN, x, y, z, XRange, YRange, newNeurons,
            Math.Abs(Math.Abs(z) - Math.Abs(layer.Z)) + connectionsDistance, maxConnected)
        {

        }

        public void connectOutputsTo(Layer outLayer)
        {
            foreach (int inputNeuron in this.layerNeurons)
            {
                int outputNeuron = outLayer.layerNeurons[random.Next(outLayer.layerNeurons.Count)];

                if (!parentNetwork.neurons[outputNeuron]
                    .ConnectToOutputOf(ref parentNetwork.neurons[inputNeuron]))
                {
                    Debug.WriteLine("neuron " + inputNeuron.ToString()
                             + " not connected to: " + outputNeuron.ToString());
                }
            }
        }
       
    }
}
